/*
 * usb_recv.c
 *
 * S3C2410 USB receive function
 * endpoint 1
 *
 * bushi <bushi@mizi.com>
 */

#include <arch.h>
#include "util.h"
#include "s3c2410_usb.h"
#include "usb_ctrl.h"

//#define USB_DEBUG 1

#ifdef USB_DEBUG
#define LOG(arg...) printk(__FILE__":"__FUNCTION__"(): " ##arg)
#else
#define LOG(arg...) (void)(0)
#endif

static unsigned char *ep1_buf;
static unsigned int ep1_len;
static usb_callback_t ep1_callback;
//static unsigned char *ep1_curdmabuf;
//static dma_addr_t ep1_curdmapos;
static unsigned int ep1_curdmalen;
static unsigned int ep1_remain;
//static unsigned int dmachn_rx;
//static unsigned int rx_pktsize;

void ep1_dma_callback(void *buf_id, int size)
{
    LOG("\n");

#ifdef USE_USBD_DMA
    rINDEX_REG = UD_INDEX_EP1;
    rOUT_CSR1_REG |= UD_OCSR1_PKTRDY;
#endif
}

//static void
//ep1_start(void)
//{
//    LOG("\n");
//#if 0
//    s3c2410_dma_flush_all(dmachn_rx);
//    if (!ep1_curdmalen) {
//          ep1_curdmalen = rx_pktsize;
//        if (ep1_curdmalen > ep1_remain)
//            ep1_curdmalen = ep1_remain;
//        ep1_curdmapos = pci_map_single(NULL, ep1_curdmabuf, ep1_curdmalen,
//                           PCI_DMA_FROMDEVICE);
//    }
//    s3c2410_dma_queue_buffer(dmachn_rx, NULL, ep1_curdmapos, ep1_curdmalen, DMA_BUF_RD);
//#endif
//}

static void
ep1_done(int flag)
{
    int size = ep1_len - ep1_remain;

    LOG(" start\n");

    if (!ep1_len)
        return;
#if 0
    if (ep1_curdmalen)
        pci_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen,
                 PCI_DMA_FROMDEVICE);
#endif
    ep1_len = ep1_curdmalen = 0;
    if (ep1_callback) {
        ep1_callback(flag, size);
    }
    LOG(" end\n");
}


void
ep1_stall( void )
{
    LOG(" start\n");

    rINDEX_REG = UD_INDEX_EP1;
    rOUT_CSR1_REG |= UD_OCSR1_SENDSTL; /* send stall, force stall */
    LOG(" end\n");
}

int
ep1_init(int chn)
{
//    desc_t * pd = s3c2410_usb_get_descriptor_ptr();
//
//    LOG(" start\n");
//
//    rx_pktsize = __le16_to_cpu( pd->b.ep1.wMaxPacketSize );
//
//    LOG("rx_pktsize = %d\n", rx_pktsize);
//
//    dmachn_rx = chn;
//#if 0
//    s3c2410_dma_flush_all(dmachn_rx);
//#endif
//    ep1_done(-EAGAIN);
//    LOG(" end\n");
    return 0;
}

void
ep1_reset(void)
{
//    desc_t * pd = s3c2410_usb_get_descriptor_ptr();
//    rx_pktsize = __le16_to_cpu( pd->b.ep1.wMaxPacketSize );
//#if 0
//    s3c2410_dma_flush_all(dmachn_rx);
//#endif
//
//    LOG("rx_pktsize = %d\n", rx_pktsize);
//
//    rINDEX_REG = UD_INDEX_EP1;
//    rOUT_CSR1_REG &= ~(UD_OCSR1_SENDSTL);
//
//    ep1_done(-EINTR);
}

void
ep1_int_hndlr(int udcsr)
{
//    dma_addr_t dma_addr;
    unsigned int len;
    int status;
    int recv_cnt;

    rINDEX_REG = UD_INDEX_EP1;
    status = rOUT_CSR1_REG;

    LOG("0x%02x\n", rOUT_CSR1_REG);
    LOG("0x%02x\n", rOUT_CSR2_REG);
    LOG("0x%02x\n", rIN_CSR1_REG);
    LOG("0x%02x\n", rIN_CSR2_REG);

    if (status & UD_OCSR1_SENTSTL) {
        rINDEX_REG = UD_INDEX_EP1;
        rOUT_CSR1_REG &= ~UD_OCSR1_SENTSTL;
        LOG("UD_OCSR1_SENTSTL\n");
         return;//?? !!
    }

    if (status & UD_OCSR1_PKTRDY) {
        LOG("ep1_len=%x\n",ep1_len);

        if (!ep1_len) {
            printk("usb_recv: RPC for non-existent buffer\n");
            rINDEX_REG = UD_INDEX_EP1;
            rOUT_CSR1_REG |= UD_OCSR1_FFLUSH;
            //udelay(20);
            rINDEX_REG = UD_INDEX_EP1;
            rOUT_CSR1_REG &= ~UD_OCSR1_PKTRDY;
            return;
        }
        rINDEX_REG = UD_INDEX_EP1;
        recv_cnt = ((rOUT_FIFO_CNT2_REG << 8) | rOUT_FIFO_CNT1_REG) &0xffff;
        LOG("recv_count = %d\n", recv_cnt);

        for (len = 0; len < recv_cnt; len++) {
            ep1_buf[len] = rEP1_FIFO;
            LOG("ep1_buf[%d]=%x\n", len, ep1_buf[len]);
        }


        ep1_remain -= recv_cnt;
        ep1_done(0);
        rINDEX_REG = UD_INDEX_EP1;
        rOUT_CSR1_REG &= ~UD_OCSR1_PKTRDY;


    }

}

int s3c2410_usb_recv(char *buf, int len)
{
//    printk("in s3c2410_usb_recv, len %d\n", len);
    unsigned int taskLen, i, cbread, fifocount;
    char *pData;
    u8 reg_cp2_csr;

    taskLen = len;
    pData = buf;

    rINDEX_REG = UD_INDEX_EP2;
    reg_cp2_csr = rOUT_CSR1_REG;
//    printk("rOUT_CSR1_REG %x\n", rOUT_CSR1_REG);

    while (rOUT_CSR1_REG & UD_OCSR1_PKTRDY) {
        rINDEX_REG = UD_INDEX_EP2;
        fifocount = (( rOUT_FIFO_CNT2_REG << 8) | rOUT_FIFO_CNT1_REG) & 0xffff;

//        printk("there has %d bytes in fifo\n", fifocount);

        if (taskLen < fifocount) {
            cbread = taskLen;
//            printk("!!!!!!!!!!!!DATA OVERRUN!!!!!!!!!!!!!!!!!!!!\n");
        }
        else {
            cbread = fifocount;
        }

//        printk("line %d, cbread %d\n", __LINE__, cbread);

        while(cbread--) {
             i = 0;
             do {
                  *pData = (unsigned char) rEP2_FIFO;
//                    udelay( 10 );
//                    printk("0x%x\t", *pOut);
                    i++;
             } while( ( (( rOUT_FIFO_CNT2_REG << 8) | rOUT_FIFO_CNT1_REG) & 0xffff ) != (--fifocount) && i < 10 );
             if ( i == 10 ) {
                  printk( "read_fifo(): read failure\n");
             }
             pData++;
             taskLen--;
        }

//            printk("after read there has %d bytes in fifo\n", fifocount);
        if (!fifocount) {
            rOUT_CSR1_REG &= ~UD_OCSR1_PKTRDY;
        }

        fifocount = (( rOUT_FIFO_CNT2_REG << 8) | rOUT_FIFO_CNT1_REG) & 0xffff;
        if (!taskLen) {
//            printk("after read there has %d bytes in fifo\n", fifocount);
            break;
        }
    }

    return (len-taskLen);
}

//EXPORT_SYMBOL(s3c2410_usb_recv);

void
s3c2410_usb_recv_reset(void)
{
    LOG("\n");
    ep1_reset();
}

//EXPORT_SYMBOL(s3c2410_usb_recv_reset);

void
s3c2410_usb_recv_stall(void)
{
    LOG("\n");
    ep1_stall();
}

//EXPORT_SYMBOL(s3c2410_usb_recv_stall);

